package log

import (
	"fmt"
	"os"
	"runtime"
	"time"

	ansi "github.com/logrusorgru/aurora"
)

// Line the print line
type Line struct {
	Timestamp  int64
	Timeformat string
	Title      string
	Text       string
	Trace      []byte
}

// NewLine form a new line
func NewLine(title, text string, trace []byte) *Line {
	timeformat, timestamp := currentTime()
	return &Line{
		Timestamp:  timestamp,
		Timeformat: timeformat,
		Title:      title,
		Text:       text,
		Trace:      trace,
	}
}

var level = 0

const (
	//Dev will console debug/info/success/error/fatal
	Dev = 1
	//Normal will console info/success/error/fatal
	Normal = 2
	//Exception will console only error/fatal
	Exception = 3
)

// SetLevel sets the console level
func SetLevel(l int) {
	if l > 3 {
		level = 3
		return
	}
	if l < 1 {
		level = 1
		return
	}
	level = l
	return
}

// PrintANSI print color log
func (l *Line) PrintANSI() {
	time := ansi.Magenta("[" + l.Timeformat + "]")
	switch l.Title {
	case "ERROR":
		fmt.Println(time, ansi.Bold(ansi.Red("ERROR")), l.Text)
		fmt.Println(string(l.Trace))
	case "SUCCESS":
		fmt.Println(time, ansi.Green("SUCCESS"), l.Text)
	case "FATAL":
		fmt.Println(time, ansi.Bold(ansi.Red("FATAL")), l.Text)
	case "INFO":
		fmt.Println(time, ansi.Blue("INFO"), l.Text)
	default:
		fmt.Println(time, ansi.Brown("DEBUG"), l.Text)
	}
}

func (l *Line) String() string {
	var title string
	switch l.Title {
	case "ERROR":
		title = "ERROR"
	case "SUCCESS":
		title = "SUCCESS"
	case "FATAL":
		title = "FATAL"
	case "INFO":
		title = "INFO"
	default:
		title = "DEBUG"
	}
	str := "[" + l.Timeformat + "] " + title + " " + l.Text + "\n"
	if l.Title == "ERROR" {
		str += string(l.Trace)
	}
	return str
}

func currentTime() (format string, stamp int64) {
	t := time.Now()
	format = t.Format("2006 Jan 02 | 15:04:05")
	stamp = t.Unix()
	return
}

// Debug print the debug
func Debug(v ...interface{}) {
	if level > 1 {
		return
	}
	l := NewLine("DEBUG", fmt.Sprint(v...), make([]byte, 1))
	l.PrintANSI()
}

// Debugf print the debug in format
func Debugf(format string, v ...interface{}) {
	if level > 1 {
		return
	}
	l := NewLine("DEBUG", fmt.Sprintf(format, v...), make([]byte, 1))
	l.PrintANSI()
}

// Error print the error
func Error(v ...interface{}) {
	buf := make([]byte, 1024)
	runtime.Stack(buf, false)
	l := NewLine("ERROR", fmt.Sprint(v...), buf)
	l.PrintANSI()
}

// Errorf print the error in format
func Errorf(format string, v ...interface{}) {
	buf := make([]byte, 1024)
	runtime.Stack(buf, false)
	l := NewLine("ERROR", fmt.Sprintf(format, v...), buf)
	l.PrintANSI()
}

// Fatal print the fatal
func Fatal(v ...interface{}) {
	l := NewLine("FATAL", fmt.Sprint(v...), make([]byte, 1))
	l.PrintANSI()
	os.Exit(1)
}

// Fatalf print the fatal in format
func Fatalf(format string, v ...interface{}) {
	l := NewLine("FATAL", fmt.Sprintf(format, v...), make([]byte, 1))
	l.PrintANSI()
	os.Exit(1)
}

// Success print the success
func Success(v ...interface{}) {
	if level > 2 {
		return
	}
	l := NewLine("SUCCESS", fmt.Sprint(v...), make([]byte, 1))
	l.PrintANSI()
}

// Successf print the success in format
func Successf(format string, v ...interface{}) {
	if level > 2 {
		return
	}
	l := NewLine("SUCCESS", fmt.Sprintf(format, v...), make([]byte, 1))
	l.PrintANSI()
}

// Info print the success
func Info(v ...interface{}) {
	if level > 2 {
		return
	}
	l := NewLine("INFO", fmt.Sprint(v...), make([]byte, 1))
	l.PrintANSI()
}

// Infof print the success in format
func Infof(format string, v ...interface{}) {
	if level > 2 {
		return
	}
	l := NewLine("INFO", fmt.Sprintf(format, v...), make([]byte, 1))
	l.PrintANSI()
}
